struct DirectionalLight
{
	float4 ambient;
	float4 diffuse;
	float4 specular;
	float3 direction;
	float pad;
};
struct PointLight
{
	float4 ambient;
	float4 diffuse;
	float4 specular;

	float3 position;
	float range;

	float3 attenuation;
	float pad;
};
struct SpotLight
{
	float4 ambient;
	float4 diffuse;
	float4 specular;

	float3 position;
	float range;

	float3 direction;
	float spot;

	float3 attenuation;
	float pad;
};
struct Material
{
	float4 ambient;
	float4 diffuse;
	float4 specular; // w = SpecPower
	float4 reflect;
};

void ComputeDirectionalLight(Material material, DirectionalLight light, float3 normalVector, float3 pixelToEyeDirection, out float4 ambient, out float4 diffuse, out float4 specular)
{
	ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
	diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
	specular = float4(0.0f, 0.0f, 0.0f, 0.0f);

	float3 lightVector = -light.direction;

	ambient = material.ambient * light.ambient;

	float diffuseFactor = dot(lightVector, normalVector);

	//Flatten to avoid dynamic branching
	[flatten]
	if(diffuseFactor>0.0f)
	{
		float3 v = reflect(-lightVector, normalVector);
		float specularFactor = pow(max(dot(v, pixelToEyeDirection), 0.0f), material.specular.w);
		diffuse = diffuseFactor * material.diffuse * light.diffuse;
		specular = specularFactor * material.specular * light.specular;
	}
}

void ComputePointLight(Material material, PointLight light, float3 pixelPosition, float3 normalVector, float3 pixelToEyeDirection, out float4 ambient, out float4 diffuse, out float4 specular)
{
	ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
	diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
	specular = float4(0.0f, 0.0f, 0.0f, 0.0f);

	float3 lightVector = light.position - pixelPosition;

	float distance = length(lightVector);
	if (distance > light.range)
	{
		return;
	}

	lightVector /= distance;

	ambient = material.ambient * light.ambient;

	float diffuseFactor = dot(lightVector, normalVector);

	//Flatten to avoid dynamic branching
	[flatten]
	if (diffuseFactor > 0.0f)
	{
		float3 v = reflect(-lightVector, normalVector);
		float specularFactor = pow(max(dot(v, pixelToEyeDirection), 0.0f), material.specular.w);
		diffuse = diffuseFactor * material.diffuse * light.diffuse;
		specular = specularFactor * material.specular * light.specular;
	}

	float attenuation = 1.0f / dot(light.attenuation, float3(1, distance, distance * distance));
	diffuse *= attenuation;
	specular *= attenuation;
}

void ComputeSpotLight(Material material, SpotLight light, float3 pixelPosition, float3 normalVector, float3 pixelToEyeDirection, out float4 ambient, out float4 diffuse, out float4 specular)
{
	ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
	diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
	specular = float4(0.0f, 0.0f, 0.0f, 0.0f);

	float3 lightVector = light.position - pixelPosition;

	float distance = length(lightVector);
	if (distance > light.range)
	{
		return;
	}

	lightVector /= distance;

	ambient = material.ambient * light.ambient;

	float diffuseFactor = dot(lightVector, normalVector);

	//Flatten to avoid dynamic branching
	[flatten]
	if (diffuseFactor > 0.0f)
	{
		float3 v = reflect(-lightVector, normalVector);
		float specularFactor = pow(max(dot(v, pixelToEyeDirection), 0.0f), material.specular.w);
		diffuse = diffuseFactor * material.diffuse * light.diffuse;
		specular = specularFactor * material.specular * light.specular;
	}

	float spot = pow(max(dot(-lightVector, light.direction), 0.0f), light.spot);
	float attenuation = spot / dot(light.attenuation, float3(1, distance, distance * distance));

	ambient *= spot;
	diffuse *= attenuation;
	specular *= attenuation;
}